﻿using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace Takkou.Core
{
    /** Cardinal directions to where the sprite is point to */
    public enum Dir
    {
        NORTH,
        SOUTH,
        EAST,
        WEST
    }

    /** Visual representation of the world elements.
     * Sprite extends Canvas, so the content of the sprite can be any UIElement object
     */
    public class Sprite : Canvas
    {
        private int _x, _y;
        double _offset, _angle;
        private Dir _dir;
        private UIElement _control;

        /** Create a Sprite from a static image source */
        public Sprite(ImageSource imgSrc)
            : this(new Image() { Source = imgSrc })
        { }

        /** Create a Sprite from a predefined UIElement (for instance, defined by a .xaml) */
        public Sprite(UIElement control)
        {
            _control = control;
            Children.Add(control);
            TransformGroup tg = new TransformGroup();
            tg.Children.Add(new TranslateTransform());  // element [0]
            tg.Children.Add(new RotateTransform());     // element [1]
            this.RenderTransform = tg;
            this.RenderTransformOrigin = new Point(0.5, 0.5);
        }

        /** X coordinate position in the world grid. */
        public int X
        {
            get { return _x; }
            set
            {
                if (_x != value)
                {
                    _x = value;
                    Offset = 0;
                    this.SetValue(Grid.ColumnProperty, value);
                }
            }
        }

        /** Y coordinate position in the world grid. */
        public int Y
        {
            get { return _y; }
            set
            {
                if (_y != value)
                {
                    _y = value;
                    Offset = 0;
                    this.SetValue(Grid.RowProperty, value);
                }
            }
        }

        /** Cardinal direction (N,S,E,W) of the element. */
        public Dir Direction
        {
            get { return _dir; }
            set
            {
                if (_dir != value)
                {
                    _dir = value;
                    TransformGroup tg = (TransformGroup)this.RenderTransform;
                    RotateTransform rt = (RotateTransform)tg.Children[1];
                    switch (_dir)
                    {
                        case Dir.EAST:
                            _angle = 0;
                            break;
                        case Dir.SOUTH:
                            _angle = 90;
                            break;
                        case Dir.WEST:
                            _angle = 180;
                            break;
                        case Dir.NORTH:
                            _angle = 270;
                            break;
                    }
                    rt.Angle = _angle;
                }
            }
        }

        /** Displacement in the X axis inside the grid cell. Useful for the move action. */
        public double Offset
        {
            get { return _offset; }
            set
            {
                if (_offset != value)
                {
                    _offset = value;
                    TransformGroup tg = (TransformGroup)this.RenderTransform;
                    TranslateTransform tt = (TranslateTransform)tg.Children[0];
                    tt.X = _offset;
                }
            }
        }

        /** Top position adjustment inside the grid cell */
        public double Top
        {
            get
            {
                return (double) _control.GetValue(Canvas.TopProperty);
            }
            set
            {
                _control.SetValue(Canvas.TopProperty, value);
            }
        }

        /** Left position adjustment inside the grid cell */
        public double Left
        {
            get
            {
                return (double)_control.GetValue(Canvas.LeftProperty);
            }
            set
            {
                _control.SetValue(Canvas.LeftProperty, value);
            }
        }

        /** Center of rotation in the X axis */
        public double CenterX
        {
            get
            {
                TransformGroup tg = (TransformGroup)this.RenderTransform;
                RotateTransform rt = (RotateTransform)tg.Children[1];
                return rt.CenterX;
            }
            set
            {
                TransformGroup tg = (TransformGroup)this.RenderTransform;
                RotateTransform rt = (RotateTransform)tg.Children[1];
                rt.CenterX = value;
            }
        }

        /** Center of rotation in the Y axis */
        public double CenterY
        {
            get
            {
                TransformGroup tg = (TransformGroup)this.RenderTransform;
                RotateTransform rt = (RotateTransform)tg.Children[1];
                return rt.CenterY;
            }
            set
            {
                TransformGroup tg = (TransformGroup)this.RenderTransform;
                RotateTransform rt = (RotateTransform)tg.Children[1];
                rt.CenterY = value;
            }
        }

        /** Move the sprite according to its direction.
         * It returns true while the move does not go beyond the grid cell limit (max).
         * When the max value is reached, it move towards a new grid cell and return false.
         */
        public bool move(double step, double max)
        {
            if (Offset > max)
            {
                switch (Direction)
                {
                    case Dir.NORTH:
                        Y--;
                        return false;
                    case Dir.SOUTH:
                        Y++;
                        return false;
                    case Dir.EAST:
                        X++;
                        return false;
                    case Dir.WEST:
                        X--;
                        return false;
                }
            }
            Offset += step;
            return true;
        }

        /** Turn the sprite to the right.
         * It also adjust the sprite direction when the rotation goes beyond 90 degrees.
         */
        public bool TurnRight (double angle)
        {
            double newAngle = _angle + angle;
            if (_angle < 360 && newAngle > 360)
                Direction = Dir.EAST;
            else if (_angle < 270 && newAngle > 270)
                Direction = Dir.NORTH;
            else if (_angle < 180 && newAngle > 180)
                Direction = Dir.WEST;
            else if (_angle < 90 && newAngle > 90)
                Direction = Dir.SOUTH;
            else
            {
                TransformGroup tg = (TransformGroup)this.RenderTransform;
                RotateTransform rt = (RotateTransform)tg.Children[1];
                _angle = newAngle;
                rt.Angle = _angle;
                return true;
            }
            return false;
        }

        /** Turn the sprite left.
         * It also adjust the sprite direction when the rotation goes beyond 90 degrees.
         */
        public bool TurnLeft (double angle)
        {
            if (_angle < 0)
                _angle += 360;

            double newAngle = _angle - angle;
            if (_angle > 0 && newAngle < 0)
                Direction = Dir.EAST;
            else if (_angle > 90 && newAngle < 90)
                Direction = Dir.SOUTH;
            else if (_angle > 180 && newAngle < 180)
                Direction = Dir.WEST;
            else if (_angle > 270 && newAngle < 270)
                Direction = Dir.NORTH;
            else
            {
                TransformGroup tg = (TransformGroup)this.RenderTransform;
                RotateTransform rt = (RotateTransform)tg.Children[1];
                _angle = newAngle;
                rt.Angle = _angle;
                return true;
            }
            return false;
        }
    }
}
